#include "canvas.h"
#include <QMouseEvent>
#include <QApplication>

const int Canvas::CREATE = 0;
const int Canvas::EDIT = 1;

Canvas::Canvas(double epsilon, const QString& double_click, int num_backups,
               const QMap<QString, bool>& crosshair)
	: epsilon(epsilon), double_click(double_click), num_backups(num_backups),
	  _crosshair(crosshair)
{
	if (!(double_click == "" || double_click == "close"))
	{
		// raise ValueError(
		//     "Unexpected value for double_click event:
		//     {}".format(self.double_click)
		//     )
	}

	line = new Shape();
	_painter = new QPainter();
	for (auto& menu : menus)
	{
		menu = new QMenu();
	}

	// Set widget options.
	setMouseTracking(true);
	setFocusPolicy(Qt::WheelFocus);
}

Canvas::~Canvas()
{
	//delete line;
	delete _painter;
	for (auto& menu : menus)
	{
		delete menu;
	}
}

bool Canvas::fillDrawing() { return _fill_drawing; }

void Canvas::setFillDrawing(bool value) { _fill_drawing = value; }

QString Canvas::createMode() { return _createMode; }

void Canvas::setCreateMode(QString value)
{
	QList<QString> valid_modes = {
		"polygon", "rectangle", "circle", "line",
		"point", "linestrip", "ai_polygon", "ai_mask",
	};

	if (!valid_modes.contains(value))
	{
		throw std::invalid_argument("Unsupported createMode: " +
			value.toStdString());
	}
	_createMode = value;
}

void Canvas::initializeAiModel(const QString& name)
{
}

void Canvas::storeShapes()
{
	// 创建一个用于备份当前 shapes 的列表
	QList<Shape*> shapesBackup;
	for (Shape* shape : shapes)
	{
		// 使用拷贝构造函数创建 Shape 对象的副本
		shapesBackup.append(new Shape(*shape));
	}

	// 检查是否超过了备份数量的限制
	if (shapesBackups.size() > num_backups)
	{
		// 保留最后 num_backups + 1 个备份（与 Python 代码对应）
		while (shapesBackups.size() > num_backups + 1)
		{
			// 移除最早的备份
			QList<Shape*> oldBackup = shapesBackups.takeFirst();
			// 释放旧备份中的 Shape 对象，防止内存泄漏
			qDeleteAll(oldBackup);
			// 清空列表（QList 是值类型，不需要显式删除）
			oldBackup.clear();
		}
	}

	// 添加当前的备份到 shapesBackups
	shapesBackups.append(shapesBackup);
}

bool Canvas::isShapeRestorable()
{
	// We save the state AFTER each edit (not before) so for an
	// edit to be undoable, we expect the CURRENT and the PREVIOUS state
	// to be in the undo stack.
	if (shapesBackups.size() < 2)
	{
		return false;
	}
	return true;
}

void Canvas::restoreShape()
{
	// This does _part_ of the job of restoring shapes.
	// The complete process is also done in app.cpp::undoShapeEdit,
	// app.cpp::loadShapes, and our own Canvas::loadShapes function.

	if (!isShapeRestorable())
	{
		return;
	}

	// Remove the latest backup (we don't need it for restoring)
	QList<Shape*> latestBackup = shapesBackups.takeLast();
	// Since we are not using latestBackup, we need to delete its contents
	qDeleteAll(latestBackup);
	latestBackup.clear();

	// Remove the previous backup, which we'll use to restore shapes
	QList<Shape*> shapesBackup = shapesBackups.takeLast();

	// Before assigning shapesBackup to shapes, we need to release existing shapes
	qDeleteAll(shapes);
	shapes.clear();

	// Assign the backup shapes to the current shapes
	// Note: We need to create copies of the shapes to avoid dangling pointers
	for (Shape* shape : shapesBackup)
	{
		Shape* newShape = new Shape(*shape); // Assuming Shape has a copy constructor
		shapes.append(newShape);
	}

	// Clean up the shapesBackup to prevent memory leaks
	qDeleteAll(shapesBackup);
	shapesBackup.clear();

	// Clear the list of selected shapes
	selectedShapes.clear();

	// Set all shapes to not selected
	for (Shape* shape : shapes)
	{
		shape->selected = false;
	}

	// Update the canvas to reflect changes
	update();
}

void Canvas::enterEvent(QEnterEvent* event)
{
	overrideCursor(_cursor);
}

void Canvas::leaveEvent(QEvent* event)
{
	unHighlight();
	restoreCursor();
}

void Canvas::focusOutEvent(QEvent* event)
{
	restoreCursor();
}

bool Canvas::isVisible(Shape* shape) const
{
	// Returns the visibility of the shape,
	// defaulting to true if the shape is not found in the map
	return visible.value(shape, true);
}

bool Canvas::drawing()
{
	return mode == CREATE;
}

bool Canvas::editing()
{
	return mode == EDIT;
}


void Canvas::setEditing(bool value)
{
	mode = value ? EDIT : CREATE;
	if (mode == EDIT)
	{
		// CREATE -> EDIT
		repaint();
	}
	else
	{
		// EDIT -> CREATE
		unHighlight();
		deSelectShape();
	}
}

void Canvas::unHighlight()
{
	if (hShape)
	{
		hShape->highlightClear();
		update();
	}
	prevhShape = hShape;
	prevhVertex = hVertex;
	prevhEdge = hEdge;
	hShape = nullptr;
	hVertex = -1;
	hEdge = -1;
}

bool Canvas::selectedVertex() const
{
	return hVertex != -1;
}

bool Canvas::selectedEdge() const
{
	return hEdge != -1;
}

void Canvas::mouseMoveEvent(QMouseEvent* event)
{
	// 更新线条的最后一点和当前坐标
	QPointF pos = transformPos(event->position());

	emit mouseMoved(pos);

	prevMovePoint = pos;
	restoreCursor();

	bool isShiftPressed = event->modifiers() & Qt::ShiftModifier;

	// 多边形绘制
	if (drawing())
	{
		if (createMode() == "ai_polygon" || createMode() == "ai_mask")
		{
			line->shape_type = "points";
		}
		else
		{
			line->shape_type = createMode();
		}

		overrideCursor(CURSOR_DRAW);

		if (!current)
		{
			repaint(); // 绘制十字光标
			return;
		}

		if (outOfPixmap(pos))
		{
			// 不允许用户在 pixmap 外绘制，将点投影到 pixmap 的边缘
			pos = intersectionPoint(current->points.last(), pos);
		}
		else if (snapping &&
			current->points.size() > 1 &&
			createMode() == "polygon" &&
			closeEnough(pos, current->points.first()))
		{
			// 吸附到起始点，并改变颜色以提示用户
			pos = current->points.first();
			overrideCursor(CURSOR_POINT);
			current->highlightVertex(0, Shape::NEAR_VERTEX);
		}

		if (createMode() == "polygon" || createMode() == "linestrip")
		{
			line->points.clear();
			line->points.append(current->points.last());
			line->points.append(pos);
			line->point_labels.clear();
			line->point_labels.append(1);
			line->point_labels.append(1);
		}
		else if (createMode() == "ai_polygon" || createMode() == "ai_mask")
		{
			line->points.clear();
			line->points.append(current->points.last());
			line->points.append(pos);
			line->point_labels.clear();
			line->point_labels.append(current->point_labels.last());
			line->point_labels.append(isShiftPressed ? 0 : 1);
		}
		else if (createMode() == "rectangle")
		{
			line->points.clear();
			line->points.append(current->points.first());
			line->points.append(pos);
			line->point_labels.clear();
			line->point_labels.append(1);
			line->point_labels.append(1);
			line->close();
		}
		else if (createMode() == "circle")
		{
			line->points.clear();
			line->points.append(current->points.first());
			line->points.append(pos);
			line->point_labels.clear();
			line->point_labels.append(1);
			line->point_labels.append(1);
			line->shape_type = "circle";
		}
		else if (createMode() == "line")
		{
			line->points.clear();
			line->points.append(current->points.first());
			line->points.append(pos);
			line->point_labels.clear();
			line->point_labels.append(1);
			line->point_labels.append(1);
			line->close();
		}
		else if (createMode() == "point")
		{
			line->points.clear();
			line->points.append(current->points.first());
			line->point_labels.clear();
			line->point_labels.append(1);
			line->close();
		}

		// 确保点数和标签数量相等
		Q_ASSERT(line->points.size() == line->point_labels.size());

		repaint();
		current->highlightClear();
		return;
	}

	// 多边形复制移动
	if (event->buttons() & Qt::RightButton)
	{
		if (!selectedShapesCopy.isEmpty() && !prevPoint.isNull())
		{
			overrideCursor(CURSOR_MOVE);
			boundedMoveShapes(selectedShapesCopy, pos);
			repaint();
		}
		else if (!selectedShapes.isEmpty())
		{
			selectedShapesCopy.clear();
			for (Shape* shape : selectedShapes)
			{
				selectedShapesCopy.append(shape->copy());
			}
			repaint();
		}
		return;
	}

	// 多边形/顶点移动
	if (event->buttons() & Qt::LeftButton)
	{
		if (selectedVertex())
		{
			boundedMoveVertex(pos);
			repaint();
			movingShape = true;
		}
		else if (!selectedShapes.isEmpty() && !prevPoint.isNull())
		{
			overrideCursor(CURSOR_MOVE);
			boundedMoveShapes(selectedShapes, pos);
			repaint();
			movingShape = true;
		}
		return;
	}

	// 仅在画布上悬停，有两种可能：
	// - 高亮形状
	// - 高亮顶点
	// 相应地更新形状/顶点的填充和工具提示
	setToolTip(tr("Image"));

	// 倒序遍历形状，以便首先检测上层形状
	for (auto it = shapes.crbegin(); it != shapes.crend(); ++it)
	{
		Shape* shape = *it;
		if (!isVisible(shape))
			continue;

		// 寻找附近的顶点进行高亮，如果失败，检查是否在形状内部
		int index = shape->nearestVertex(pos, epsilon);
		int indexEdge = shape->nearestEdge(pos, epsilon);

		if (index != -1)
		{
			if (selectedVertex())
			{
				hShape->highlightClear();
			}
			prevhVertex = hVertex = index;
			prevhShape = hShape = shape;
			prevhEdge = hEdge;
			hEdge = -1;
			shape->highlightVertex(index, Shape::MOVE_VERTEX);
			overrideCursor(CURSOR_POINT);
			setToolTip(tr("Click & Drag to move point\nALT + SHIFT + Click to delete point"));
			setStatusTip(toolTip());
			update();
			break;
		}
		else if (indexEdge != -1 && shape->canAddPoint())
		{
			if (selectedVertex())
			{
				hShape->highlightClear();
			}
			prevhVertex = hVertex;
			hVertex = -1;
			prevhShape = hShape = shape;
			prevhEdge = hEdge = indexEdge;
			overrideCursor(CURSOR_POINT);
			setToolTip(tr("ALT + Click to create point"));
			setStatusTip(toolTip());
			update();
			break;
		}
		else if (shape->containsPoint(pos))
		{
			if (selectedVertex())
			{
				hShape->highlightClear();
			}
			prevhVertex = hVertex;
			hVertex = -1;
			prevhShape = hShape = shape;
			prevhEdge = hEdge;
			hEdge = -1;
			setToolTip(tr("Click & drag to move shape '%1'").arg(shape->label));
			setStatusTip(toolTip());
			overrideCursor(CURSOR_GRAB);
			update();
			break;
		}
	}

	if (!hShape)
	{
		// 没有找到匹配的形状，清除高亮，重置状态
		unHighlight();
	}

	emit vertexSelected(hVertex != -1);
}

void Canvas::addPointToEdge()
{
	Shape* shape = prevhShape;
	int index = prevhEdge;
	QPointF point = prevMovePoint;

	if (shape == nullptr || index == -1)
	{
		return;
	}

	shape->insertPoint(index, point);
	shape->highlightVertex(index, Shape::MOVE_VERTEX);
	hShape = shape;
	hVertex = index;
	hEdge = -1;
	movingShape = true;
}

void Canvas::removeSelectedPoint()
{
	Shape* shape = prevhShape;
	int index = prevhVertex;

	if (shape == nullptr || index == -1)
	{
		return;
	}

	shape->removePoint(index);
	shape->highlightClear();
	hShape = shape;
	prevhVertex = -1;
	movingShape = true; // 保存更改
}

void Canvas::mousePressEvent(QMouseEvent* ev)
{
	QPointF pos = transformPos(ev->position());

	bool isShiftPressed = ev->modifiers() & Qt::ShiftModifier;

	if (ev->button() == Qt::LeftButton)
	{
		if (drawing())
		{
			if (current)
			{
				// 添加点到现有形状
				if (createMode() == "polygon")
				{
					current->addPoint(line->points[1]);
					line->points[0] = current->points.last();
					if (current->isClosed())
					{
						finalise();
					}
				}
				else if (createMode() == "rectangle" || createMode() == "circle" || createMode() == "line")
				{
					Q_ASSERT(current->points.size() == 1);
					current->points = line->points;
					finalise();
				}
				else if (createMode() == "linestrip")
				{
					current->addPoint(line->points[1]);
					line->points[0] = current->points.last();
					if (ev->modifiers() == Qt::ControlModifier)
					{
						finalise();
					}
				}
				else if (createMode() == "ai_polygon" || createMode() == "ai_mask")
				{
					current->addPoint(line->points[1], line->point_labels[1]);
					line->points[0] = current->points.back();
					line->point_labels[0] = current->point_labels.back();
					if (ev->modifiers() & Qt::ControlModifier)
					{
						finalise();
					}
				}
			}
			else if (!outOfPixmap(pos))
			{
				// 创建新形状
				QString shapeType = (createMode() == "ai_polygon" || createMode() == "ai_mask")
					                    ? "points"
					                    : createMode();
				current = new Shape(shapeType);
				current->addPoint(pos, isShiftPressed ? 0 : 1);

				if (createMode() == "point")
				{
					finalise();
				}
				else if ((createMode() == "ai_polygon" || createMode() == "ai_mask") &&
					(ev->modifiers() & Qt::ControlModifier))
				{
					finalise();
				}
				else
				{
					if (createMode() == "circle")
					{
						current->setShapeType("circle");
					}
					line->points.clear();
					line->points.append(pos);
					line->points.append(pos);
					line->point_labels.clear();
					if ((createMode() == "ai_polygon" || createMode() == "ai_mask") && isShiftPressed)
					{
						line->point_labels.append(0);
						line->point_labels.append(0);
					}
					else
					{
						line->point_labels.append(1);
						line->point_labels.append(1);
					}
					setHiding();
					emit drawingPolygon(true);
					update();
				}
			}
		}
		else if (editing())
		{
			if (selectedEdge() && ev->modifiers() == Qt::AltModifier)
			{
				addPointToEdge();
			}
			else if (selectedVertex() && ev->modifiers() == (Qt::AltModifier | Qt::ShiftModifier))
			{
				removeSelectedPoint();
			}

			bool groupMode = ev->modifiers() == Qt::ControlModifier;
			selectShapePoint(pos, groupMode);
			prevPoint = pos;
			repaint();
		}
	}
	else if (ev->button() == Qt::RightButton && editing())
	{
		bool groupMode = ev->modifiers() == Qt::ControlModifier;
		if (selectedShapes.isEmpty() || (hShape && !selectedShapes.contains(hShape)))
		{
			selectShapePoint(pos, groupMode);
			repaint();
		}
		prevPoint = pos;
	}
}

void Canvas::mouseReleaseEvent(QMouseEvent* ev)
{
	if (ev->button() == Qt::RightButton)
	{
		QMenu* menu = menus[!selectedShapesCopy.empty()];
		restoreCursor();
		if (!menu->exec(mapToGlobal(ev->pos())) && !selectedShapesCopy.empty())
		{
			// 通过删除临时副本来取消移动
			selectedShapesCopy.clear();
			repaint();
		}
	}
	else if (ev->button() == Qt::LeftButton)
	{
		if (editing())
		{
			if (hShape != nullptr && hShapeIsSelected && !movingShape)
			{
				// 发射 selectionChanged 信号，传递除了 hShape 之外的已选形状
				QList<Shape*> newSelection;
				for (auto& shape : selectedShapes)
				{
					if (shape != hShape)
						newSelection.push_back(shape);
				}
				emit selectionChanged(newSelection);
			}
		}
	}

	if (movingShape && hShape)
	{
		auto it = std::find(shapes.begin(), shapes.end(), hShape);
		if (it != shapes.end())
		{
			int index = (int)std::distance(shapes.begin(), it);
			if (shapesBackups.back()[index]->points != shapes[index]->points)
			{
				storeShapes();
				emit shapeMoved();
			}
		}
		movingShape = false;
	}
}

bool Canvas::endMove(bool copy)
{
	Q_ASSERT(!selectedShapes.empty() && !selectedShapesCopy.empty());
	Q_ASSERT(selectedShapesCopy.size() == selectedShapes.size());

	if (copy)
	{
		for (size_t i = 0; i < selectedShapesCopy.size(); ++i)
		{
			Shape* shape = selectedShapesCopy[i];
			shapes.push_back(shape);
			selectedShapes[i]->selected = false;
			selectedShapes[i] = shape;
		}
	}
	else
	{
		for (size_t i = 0; i < selectedShapesCopy.size(); ++i)
		{
			selectedShapes[i]->points = selectedShapesCopy[i]->points;
		}
	}

	selectedShapesCopy.clear();
	repaint();
	storeShapes();
	return true;
}

void Canvas::hideBackroundShapes(bool value)
{
	hideBackround = value;
	if (!selectedShapes.empty())
	{
		// 只有在有当前选中的形状时才隐藏其他形状。
		// 否则，用户将无法选择形状。
		setHiding(true);
		update();
	}
}

void Canvas::setHiding(bool enable)
{
	_hideBackround = enable && hideBackround;
}

bool Canvas::canCloseShape()
{
	return drawing() && (
		(current != nullptr && current->points.size() > 2)
		|| (createMode() == "ai_polygon" || createMode() == "ai_mask")
	);
}

void Canvas::mouseDoubleClickEvent(QMouseEvent* ev)
{
	if (double_click != "close")
		return;

	if ((createMode() == "polygon" && canCloseShape()) ||
		createMode() == "ai_polygon" || createMode() == "ai_mask")
	{
		finalise();
	}
}

void Canvas::selectShapes(const QList<Shape*>& shapes)
{
	setHiding();
	emit selectionChanged(shapes);
	update();
}

void Canvas::selectShapePoint(const QPointF& point, bool multiple_selection_mode)
{
	// 如果有顶点被选中
	if (selectedVertex())
	{
		int index = hVertex;
		Shape* shape = hShape;
		shape->highlightVertex(index, Shape::MOVE_VERTEX);
	}
	else
	{
		// 逆序遍历形状列表
		for (auto it = shapes.rbegin(); it != shapes.rend(); ++it)
		{
			Shape* shape = *it;
			if (isVisible(shape) && shape->containsPoint(point))
			{
				setHiding();
				if (!selectedShapes.contains(shape))
				{
					if (multiple_selection_mode)
					{
						QList<Shape*> newSelection = selectedShapes;
						newSelection.append(shape);
						emit selectionChanged(newSelection);
					}
					else
					{
						QList<Shape*> newSelection;
						newSelection.append(shape);
						emit selectionChanged(newSelection);
					}
					hShapeIsSelected = false;
				}
				else
				{
					hShapeIsSelected = true;
				}
				calculateOffsets(point);
				return;
			}
		}
		// 如果没有找到满足条件的形状，则取消选择
		deSelectShape();
	}
}

void Canvas::calculateOffsets(const QPointF& point)
{
	qreal left = pixmap.width() - 1;
	qreal right = 0;
	qreal top = pixmap.height() - 1;
	qreal bottom = 0;

	for (Shape* s : selectedShapes)
	{
		QRectF rect = s->boundingRect();
		if (rect.left() < left)
			left = rect.left();
		if (rect.right() > right)
			right = rect.right();
		if (rect.top() < top)
			top = rect.top();
		if (rect.bottom() > bottom)
			bottom = rect.bottom();
	}

	qreal x1 = left - point.x();
	qreal y1 = top - point.y();
	qreal x2 = right - point.x();
	qreal y2 = bottom - point.y();

	QPointF offset1(x1, y1);
	QPointF offset2(x2, y2);

	offsets[0] = offset1;
	offsets[1] = offset2;
}

void Canvas::boundedMoveVertex(const QPointF& pos)
{
	int index = hVertex;
	Shape* shape = hShape;
	QPointF point = (*shape)[index];

	QPointF newPos = pos;
	if (outOfPixmap(pos))
	{
		newPos = intersectionPoint(point, pos);
	}
	shape->moveVertexBy(index, newPos - point);
}

bool Canvas::boundedMoveShapes(const QList<Shape*>& shapes, QPointF pos)
{
	if (outOfPixmap(pos))
		return false; // 不需要移动

	QPointF o1 = pos + offsets[0];
	if (outOfPixmap(o1))
	{
		pos -= QPointF(
			std::min(0.0, o1.x()),
			std::min(0.0, o1.y())
		);
	}

	QPointF o2 = pos + offsets[1];
	if (outOfPixmap(o2))
	{
		pos += QPointF(
			std::min(0.0, pixmap.width() - o2.x()),
			std::min(0.0, pixmap.height() - o2.y())
		);
	}

	// 以下代码被注释掉，因为在 Python 代码中也是注释的
	// 但如果需要可以取消注释
	// calculateOffsets(selectedShapes, pos);

	QPointF dp = pos - prevPoint;
	if (!dp.isNull())
	{
		for (Shape* shape : shapes)
		{
			shape->moveBy(dp);
		}
		prevPoint = pos;
		return true;
	}

	return false;
}

void Canvas::deSelectShape()
{
	if (!selectedShapes.isEmpty())
	{
		setHiding(false);
		emit selectionChanged(QList<Shape*>());
		hShapeIsSelected = false;
		update();
	}
}

QList<Shape*> Canvas::deleteSelected()
{
	QList<Shape*> deletedShapes;

	if (!selectedShapes.isEmpty())
	{
		for (Shape* shape : selectedShapes)
		{
			shapes.removeOne(shape);
			deletedShapes.append(shape);
			delete shape; // Free memory if no longer needed
		}
		storeShapes();
		selectedShapes.clear();
		update();
	}
	return deletedShapes;
}

void Canvas::deleteShape(Shape* shape)
{
	if (selectedShapes.contains(shape))
	{
		selectedShapes.removeOne(shape);
	}

	if (shapes.contains(shape))
	{
		shapes.removeOne(shape);
		delete shape; // Free the memory allocated to the shape
	}

	storeShapes();
	update();
}

void Canvas::paintEvent(QPaintEvent* event)
{
	Q_UNUSED(event);

	if (pixmap.isNull())
	{
		QWidget::paintEvent(event); // Or QGraphicsView::paintEvent(event) if inheriting from QGraphicsView
		return;
	}

	QPainter p(this);

	p.setRenderHint(QPainter::Antialiasing);
	//    p.setRenderHint(QPainter::HighQualityAntialiasing);
	p.setRenderHint(QPainter::SmoothPixmapTransform);

	p.scale(scale, scale);
	p.translate(offsetToCenter());

	p.drawPixmap(0, 0, pixmap);

	p.scale(1.0 / scale, 1.0 / scale);

	// Draw crosshair
	if (_crosshair[createMode()] && drawing() && !prevMovePoint.isNull() && !outOfPixmap(prevMovePoint))
	{
		p.setPen(QColor(0, 0, 0));
		int x = static_cast<int>(prevMovePoint.x() * scale);
		int y = static_cast<int>(prevMovePoint.y() * scale);
		p.drawLine(0, y, width() - 1, y);
		p.drawLine(x, 0, x, height() - 1);
	}

	Shape::scale = scale;

	for (Shape* shape : shapes)
	{
		if ((shape->selected || !_hideBackround) && isVisible(shape))
		{
			shape->fill = shape->selected || shape == hShape;
			shape->paint(&p);
		}
	}

	if (current)
	{
		current->paint(&p);
		if (line)
		{
			line->paint(&p);
		}
	}

	// Draw selected shapes copy if any
	if (!selectedShapesCopy.isEmpty())
	{
		for (Shape* s : selectedShapesCopy)
		{
			s->paint(&p);
		}
	}

	// Handle different create modes
	if (fillDrawing() && createMode() == "polygon" && current && current->points.size() >= 2)
	{
		Shape* drawingShape = current->copy();
		if (drawingShape->fill_color.alpha() == 0)
		{
			qWarning() << "fill_drawing=true, but fill_color is transparent, so forcing to be opaque.";
			QColor color = drawingShape->fill_color;
			color.setAlpha(64);
			drawingShape->fill_color = color;
		}
		drawingShape->addPoint(line->points[1]);
		drawingShape->fill = true;
		drawingShape->paint(&p);
		delete drawingShape;
	}
	else if (createMode() == "ai_polygon" && current)
	{
		Shape* drawingShape = current->copy();
		drawingShape->addPoint(line->points[1], line->point_labels[1]);
		QVector<QPointF> predictedPoints;
		//        QVector<QPointF> predictedPoints = aiModel->predictPolygonFromPoints(drawingShape->points, drawingShape->point_labels);
		if (predictedPoints.size() > 2)
		{
			drawingShape->setShapeRefined("polygon", predictedPoints, QVector<int>(predictedPoints.size(), 1));
			drawingShape->fill = fillDrawing();
			drawingShape->selected = true;
			drawingShape->paint(&p);
		}
		delete drawingShape;
	}
	else if (createMode() == "ai_mask" && current)
	{
		Shape* drawingShape = current->copy();
		drawingShape->addPoint(line->points[1], line->point_labels[1]);
		cv::Mat mask;
		//        QImage mask = aiModel->predictMaskFromPoints(drawingShape->points, drawingShape->point_labels);

		//        QRect boundingBox = maskBoundingBox(mask);
		QRect boundingBox;
		drawingShape->setShapeRefined("mask", {boundingBox.topLeft(), boundingBox.bottomRight()}, {1, 1}, mask);
		drawingShape->selected = true;
		drawingShape->paint(&p);
		delete drawingShape;
	}

	// End the painter
	p.end();
}

QPointF Canvas::transformPos(const QPointF& point) const
{
	// Convert from widget-logical coordinates to painter-logical ones.
	return (point / scale) - offsetToCenter();
}

QPointF Canvas::offsetToCenter() const
{
	double s = scale;
	QSize area = size(); // Assuming Canvas inherits from QWidget
	double w = pixmap.width() * s;
	double h = pixmap.height() * s;
	double aw = area.width();
	double ah = area.height();
	double x = (aw > w) ? ((aw - w) / (2 * s)) : 0;
	double y = (ah > h) ? ((ah - h) / (2 * s)) : 0;
	return QPointF(x, y);
}

bool Canvas::outOfPixmap(const QPointF& point) const
{
	double w = static_cast<double>(pixmap.width());
	double h = static_cast<double>(pixmap.height());
	double x = point.x();
	double y = point.y();
	return !(x >= 0 && x <= w - 1 && y >= 0 && y <= h - 1);
}

void Canvas::finalise()
{
	// Ensure current shape exists
	assert(current != nullptr);

	if (createMode() == "ai_polygon")
	{
		// Convert points to polygon using the AI model
		assert(current->getShapeType() == "points");

		// Extract points and labels from current shape
		QVector<QPointF> currentPoints = current->points;
		QVector<int> pointLabels = current->point_labels;

		// Prepare data for AI model
		QVector<QVector<double>> pointsForAI;
		for (const QPointF& point : currentPoints)
		{
			pointsForAI.append({point.x(), point.y()});
		}

		// Predict polygon points using AI model
		QVector<QPointF> predictedPoints;
		//        QVector<QPointF> predictedPoints = aiModel->predictPolygonFromPoints(pointsForAI, pointLabels);

		// Update current shape with the predicted polygon
		QVector<int> newPointLabels(predictedPoints.size(), 1); // Label '1' for all points
		current->setShapeRefined("polygon", predictedPoints, newPointLabels);
	}
	else if (createMode() == "ai_mask")
	{
		// Convert points to mask using the AI model
		assert(current->getShapeType() == "points");

		// Extract points and labels
		QVector<QPointF> currentPoints = current->points;
		QVector<int> pointLabels = current->point_labels;

		// Prepare data for AI model
		QVector<QVector<double>> pointsForAI;
		for (const QPointF& point : currentPoints)
		{
			pointsForAI.append({point.x(), point.y()});
		}

		// Predict mask using AI model
		cv::Mat mask;
		//        QImage mask = aiModel->predictMaskFromPoints(pointsForAI, pointLabels);

		// Compute bounding box from mask
		//        QRect bbox = computeBoundingBoxFromMask(mask);
		QRect bbox;
		// Extract bounding box coordinates
		int x1 = bbox.left();
		int y1 = bbox.top();
		int x2 = bbox.right();
		int y2 = bbox.bottom();

		// Update current shape with the bounding box and mask
		QVector<QPointF> bboxPoints = {QPointF(x1, y1), QPointF(x2, y2)};
		QVector<int> bboxPointLabels = {1, 1}; // Label '1' for both points

		// Crop the mask to the bounding box
		cv::Rect maskRect(x1, y1, bbox.width(), bbox.height());
		cv::Mat maskCropped;
		mask(maskRect).copyTo(maskCropped);

		current->setShapeRefined("mask", bboxPoints, bboxPointLabels, maskCropped);
	}

	// Finalize the current shape
	current->close();

	// Add current shape to shapes list
	shapes.append(current);

	// Store shapes
	storeShapes();

	// Reset current shape
	current = nullptr;

	// Update visibility
	setHiding(false);

	// Emit newShape signal
	emit newShape();

	// Refresh the canvas
	update();
}

bool Canvas::closeEnough(const QPointF& p1, const QPointF& p2) const
{
	// Compute the difference vector between the points
	QPointF diff = p1 - p2;

	// Calculate the Euclidean distance between p1 and p2
	double distance = qSqrt(qPow(diff.x(), 2) + qPow(diff.y(), 2));

	// Adjust epsilon by the current scale to allow more precision when zoomed in
	double adjustedEpsilon = epsilon / scale;

	// Return true if the distance is less than the adjusted epsilon
	return distance < adjustedEpsilon;
}

QPointF Canvas::intersectionPoint(const QPointF& p1, const QPointF& p2) const
{
	// Get the size of the pixmap
	QSize size = pixmap.size();

	// Define the four corners of the pixmap in clockwise order
	QVector<QPointF> points = {
		QPointF(0, 0),
		QPointF(size.width() - 1, 0),
		QPointF(size.width() - 1, size.height() - 1),
		QPointF(0, size.height() - 1)
	};

	// x1, y1 should be in the pixmap, x2, y2 could be outside the pixmap
	double x1 = qMin(qMax(p1.x(), 0.0), static_cast<double>(size.width() - 1));
	double y1 = qMin(qMax(p1.y(), 0.0), static_cast<double>(size.height() - 1));
	double x2 = p2.x();
	double y2 = p2.y();

	QPointF adjustedP1(x1, y1);
	QPointF adjustedP2(x2, y2);

	// Get the list of intersecting edges
	QVector<std::tuple<double, int, QPointF>> intersections = intersectingEdges(adjustedP1, adjustedP2, points);

	// Find the intersection with the minimum distance
	auto minIt = std::min_element(
		intersections.begin(),
		intersections.end(),
		[](const std::tuple<double, int, QPointF>& a, const std::tuple<double, int, QPointF>& b)
		{
			return std::get<0>(a) < std::get<0>(b);
		}
	);

	if (minIt == intersections.end())
	{
		// No intersection found; return adjustedP1
		return adjustedP1;
	}

	double d = std::get<0>(*minIt);
	int i = std::get<1>(*minIt);
	QPointF intersectionPoint = std::get<2>(*minIt);

	QPointF edgeStart = points[i];
	QPointF edgeEnd = points[(i + 1) % 4];

	if (intersectionPoint == adjustedP1)
	{
		// Handle cases where previous point is on one of the edges.
		if (edgeStart.x() == edgeEnd.x())
		{
			// Vertical edge
			double x = edgeStart.x();
			double y = qMin(qMax(0.0, y2), qMax(edgeStart.y(), edgeEnd.y()));
			return QPointF(x, y);
		}
		else // edgeStart.y() == edgeEnd.y()
		{
			// Horizontal edge
			double y = edgeStart.y();
			double x = qMin(qMax(0.0, x2), qMax(edgeStart.x(), edgeEnd.x()));
			return QPointF(x, y);
		}
	}

	return intersectionPoint;
}

double distanceBetweenPoints(const QPointF& p1, const QPointF& p2)
{
	double dx = p1.x() - p2.x();
	double dy = p1.y() - p2.y();
	return std::sqrt(dx * dx + dy * dy);
}

QVector<std::tuple<double, int, QPointF>>
Canvas::intersectingEdges(const QPointF& point1, const QPointF& point2, QList<QPointF> points) const
{
	QVector<std::tuple<double, int, QPointF>> results;

	double x1 = point1.x();
	double y1 = point1.y();
	double x2 = point2.x();
	double y2 = point2.y();

	for (int i = 0; i < 4; ++i)
	{
		double x3 = points[i].x();
		double y3 = points[i].y();
		double x4 = points[(i + 1) % 4].x();
		double y4 = points[(i + 1) % 4].y();

		double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
		double nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
		double nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);

		if (denom == 0.0)
		{
			// This covers two cases:
			//   nua == nub == 0: Coincident
			//   otherwise: Parallel
			continue;
		}

		double ua = nua / denom;
		double ub = nub / denom;

		if (ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0)
		{
			double x = x1 + ua * (x2 - x1);
			double y = y1 + ua * (y2 - y1);

			QPointF m((x3 + x4) / 2.0, (y3 + y4) / 2.0);
			double distance = distanceBetweenPoints(m, point2);
			auto intersectionPoint = QPointF(x, y);
			results.append(std::make_tuple(distance, i, intersectionPoint));
		}
	}

	return results;
}

QSize Canvas::sizeHint()
{
	return minimumSizeHint();
}

QSize Canvas::minimumSizeHint()
{
	if (!pixmap.isNull())
	{
		QSize pixmapSize = pixmap.size();
		int scaledWidth = static_cast<int>(pixmapSize.width() * scale);
		int scaledHeight = static_cast<int>(pixmapSize.height() * scale);
		return QSize(scaledWidth, scaledHeight);
	}
	return QWidget::minimumSizeHint();
}

void Canvas::wheelEvent(QWheelEvent* ev)
{
	Qt::KeyboardModifiers mods = ev->modifiers();
	QPoint delta = ev->angleDelta();

	if (mods & Qt::ControlModifier)
	{
		// With Ctrl/Command key pressed, perform zoom
		emit zoomRequest(delta.y(), ev->position());
	}
	else
	{
		// Perform scroll
		emit scrollRequest(delta.x(), Qt::Horizontal);
		emit scrollRequest(delta.y(), Qt::Vertical);
	}
	ev->accept();
}

void Canvas::moveByKeyboard(const QPointF& offset)
{
	if (!selectedShapes.empty())
	{
		boundedMoveShapes(selectedShapes, prevPoint + offset);
		repaint();
		movingShape = true;
	}
}

void Canvas::keyPressEvent(QKeyEvent* ev)
{
	Qt::KeyboardModifiers modifiers = ev->modifiers();
	int key = ev->key();

	if (drawing())
	{
		if (key == Qt::Key_Escape && current)
		{
			current = nullptr;
			emit drawingPolygon(false);
			update();
		}
		else if (key == Qt::Key_Return && canCloseShape())
		{
			finalise();
		}
		else if (modifiers == Qt::AltModifier)
		{
			snapping = false;
		}
	}
	else if (editing())
	{
		if (key == Qt::Key_Up)
		{
			moveByKeyboard(QPointF(0.0, -MOVE_SPEED));
		}
		else if (key == Qt::Key_Down)
		{
			moveByKeyboard(QPointF(0.0, MOVE_SPEED));
		}
		else if (key == Qt::Key_Left)
		{
			moveByKeyboard(QPointF(-MOVE_SPEED, 0.0));
		}
		else if (key == Qt::Key_Right)
		{
			moveByKeyboard(QPointF(MOVE_SPEED, 0.0));
		}
	}
}

void Canvas::keyReleaseEvent(QKeyEvent* ev)
{
	Qt::KeyboardModifiers modifiers = ev->modifiers();
	if (drawing())
	{
		if (modifiers == Qt::NoModifier)
		{
			snapping = true;
		}
	}
	else if (editing())
	{
		if (movingShape && !selectedShapes.empty())
		{
			// Find the index of the first selected shape in the shapes vector
			auto it = std::find(shapes.begin(), shapes.end(), selectedShapes[0]);
			if (it != shapes.end())
			{
				int index = std::distance(shapes.begin(), it);
				// Compare the points of the current shape with the backup
				if (shapesBackups.back()[index]->points != (*it)->points)
				{
					storeShapes();
					emit shapeMoved();
				}
			}
			movingShape = false;
		}
	}
}

Shape* Canvas::setLastLabel(const QString& text, const std::map<QString, QString>& flags)
{
	Q_ASSERT(!text.isEmpty());
	if (shapes.empty())
	{
		// Handle the case where there are no shapes
		return nullptr;
	}

	// Set the label and flags of the last shape in the shapes vector
	Shape* lastShape = shapes.back();
	lastShape->label = text;
	lastShape->flags = flags;

	// Remove the last backup from shapesBackups
	if (!shapesBackups.empty())
	{
		shapesBackups.pop_back();
	}

	// Store the current state of shapes for undo functionality
	storeShapes();

	// Return the modified last shape
	return lastShape;
}

void Canvas::undoLastLine()
{
	Q_ASSERT(!shapes.empty());
	if (shapes.empty())
	{
		// Handle the case where there are no shapes
		return;
	}

	// Set current to the last shape and remove it from the shapes vector
	current = shapes.back();
	shapes.pop_back();

	// Set the shape as open and restore its raw state
	current->setOpen();
	current->restoreShapeRaw();

	// Handle different creation modes using string comparisons
	if (createMode() == "polygon" || createMode() == "linestrip")
	{
		if (line)
		{
			line->points.clear();
			line->points.push_back(current->points.back());
			line->points.push_back(current->points.front());
		}
	}
	else if (createMode() == "rectangle" || createMode() == "line" || createMode() == "circle")
	{
		// Keep only the first point
		if (!current->points.empty())
		{
			current->points.resize(1);
		}
	}
	else if (createMode() == "point")
	{
		current = nullptr;
	}

	emit drawingPolygon(true);
}

void Canvas::undoLastPoint()
{
	if (!current || current->isClosed())
	{
		return;
	}

	// Remove the last point from the current shape
	current->popPoint();

	if (!current->points.empty())
	{
		// Update the helper line to start from the new last point
		if (line)
		{
			line->points[0] = current->points.back();
		}
	}
	else
	{
		// If no points are left, reset the current shape
		delete current;
		current = nullptr;

		// Emit signal to indicate that drawing has stopped
		emit drawingPolygon(false);
	}

	// Redraw the canvas to reflect changes
	update();
}

void Canvas::loadPixmap(const QPixmap& pixmap, bool clear_shapes)
{
	// Set the canvas pixmap
	this->pixmap = pixmap;

	// If the AI model is available, set the image
	if (_ai_model)
	{
		// Convert QPixmap to QImage
		QImage image = pixmap.toImage();

		// Optionally, convert QImage to a format suitable for the AI model
		// Assuming _ai_model->set_image accepts QImage
		//        _ai_model->set_image(image);

		// If conversion is needed, you might do something like:
		/*
		cv::Mat mat = QImageToCvMat(image);
		_ai_model->set_image(mat);
		*/
	}

	// Clear shapes if required
	if (clear_shapes)
	{
		shapes.clear();
	}

	// Update the canvas display
	update();
}

void Canvas::loadShapes(const QList<Shape*>& shapes, bool replace)
{
	if (replace)
	{
		// If replacing, delete existing shapes to prevent memory leaks
		qDeleteAll(this->shapes);
		this->shapes.clear();

		// Replace the existing shapes with the new ones
		this->shapes = shapes;
	}
	else
	{
		// Append the new shapes to the existing ones
		this->shapes.append(shapes);
	}

	// Store the shapes (implementation depends on your application)
	storeShapes();

	// Reset current interactions
	current = nullptr;
	hShape = nullptr;
	hVertex = -1;
	hEdge = -1;

	// Update the canvas display
	update();
}

void Canvas::setShapeVisible(Shape* shape, bool value)
{
	visible[shape] = value;
	update();
}

void Canvas::overrideCursor(const QCursor& cursor)
{
	restoreCursor();
	_cursor = cursor;
	QApplication::setOverrideCursor(cursor);
}

void Canvas::restoreCursor()
{
	QApplication::restoreOverrideCursor();
}

void Canvas::resetState()
{
	restoreCursor();
	pixmap = QPixmap();
	shapesBackups.clear();
	update();
}
